
import os
import sys

from rpyc.utils.server import ThreadedServer

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

temp_dir = os.path.abspath(__file__)
for i in range(4):
    temp_dir = os.path.dirname(temp_dir)
sys.path.append(temp_dir)

import dexen
from dexen.system.server.db_schema import Task, Population, Schema, Base 
from dexen.system import constants, util
from dexen.system.base import population

class BaseService(dexen.Service):
    def on_connect(self):
        self.session = Session()       
            
    def initialize(self, schema_name=None, pop_name=None, task_name=None):
        self.schema_name = schema_name
        self.pop_name = pop_name
        self.task_name = task_name
        self.population = self.get_pop_obj()
        self.max_inds = self.population.max_inds
        self.task = self.get_task_obj()   
        
    def get_pop_obj(self, schema_name=None, pop_name=None):
        session = self.session
        if not schema_name: schema_name = self.schema_name
        if not pop_name: pop_name = self.pop_name
        pop_obj = session.query(Population).join(Schema).\
                    filter(Population.name == pop_name).\
                    filter(Schema.name == schema_name).first()
        return pop_obj       
    
    def get_task_obj(self, schema_name=None, pop_name=None, task_name=None):
        session = self.session
        if not schema_name: schema_name = self.schema_name
        if not pop_name: pop_name = self.pop_name
        if not task_name: task_name = self.task_name
        task_obj = session.query(Task).join(Population).join(Schema).\
                        filter(Schema.name == schema_name).\
                        filter(Population.name == pop_name).\
                        filter(Task.name == task_name).first()
        return task_obj  


class DBService(BaseService):
    ALIASES = ["BACKEND"]
    CONFIG_FILE = "config.py"
    TEMP_DIRNAME = "tmp_db_mgr"
     
    def on_connect(self):
        BaseService.on_connect(self)        
        print "Somebody connected in FrontendService"
        
    def on_disconnect(self):
        pass

    def reset_all_databases(self):
        Base.metadata.drop_all(engine)
        Base.metadata.create_all(engine)

    def _run_config(self, schema_name, zip_data):
        temp_dir = os.path.abspath(DBService.TEMP_DIRNAME)  
        if not os.path.exists(temp_dir):
            os.mkdir(temp_dir)        
        config_file = DBService.CONFIG_FILE
        util.unzip(zip_data, temp_dir)  
        
        prevdir = os.getcwd()
        os.chdir(temp_dir)
        sys.path.append(temp_dir)
        
        """ setting some globals in base.population """
        population.schema_name = schema_name 
        population.backend = self 
        
        execfile(config_file)
        os.chdir(prevdir)
        sys.path = sys.path[:-1]
        # The tasks to test correctness
        #print self.get_task_names(schema_name, "pop_synch")
    
    def has_schema(self, schema_name):
        schema_names = self.get_schema_names()
        return (schema_name in schema_names)
           
    def set_schema(self, schema_name, zip_data, last_modified_time):
        session = self.session
        schema = Schema(schema_name, zip_data, last_modified_time)
        session.add(schema)
        session.commit()
        self._run_config(schema_name, zip_data)

        
    def get_schemas(self):
        session = self.session
        schemas = []
        for schema in session.query(Schema.name, Schema.zip_data, 
                                    Schema.last_modified_time):
            schemas.append(schema)
        session.commit()
        return schemas 
    
    def get_schema(self, schema_name):
        session = self.session
        schema = session.query(Schema.zip_data, Schema.last_modified_time).\
                        filter(Schema.name == schema_name).first()
        session.commit()
        return schema
    
    def get_schema_ts(self, schema_name):
        session = self.session
        schema_timestamp = session.query(Schema.last_modified_time).\
                        filter(Schema.name == schema_name).scalar()
        session.commit()
        return schema_timestamp
        
    def get_schema_names(self):
        session = self.session
        schema_names = []
        for schema_name in session.query(Schema.name).order_by(Schema.name):
            schema_names.append(schema_name[0])
        session.commit()
        return schema_names
    
    def stop_population(self, schema_name, pop_name):
        session = self.session
        population = self.get_pop_obj(schema_name, pop_name)
        population.is_stopped = True
        session.commit()    
    
    def delete_population(self, schema_name, pop_name):
        session = Session()
        population = self.get_pop_obj(schema_name, pop_name)
        session.delete(population)
        session.commit()
    
    def stop_task(self, schema_name, pop_name, task_name):
        session = self.session
        print "I am stopping the task:", task_name, "*"*10
        task = self.get_task_obj(schema_name, pop_name, task_name)
        task.is_stopped = True
        session.commit()
    
    def set_population(self, schema_name, pop_name, creation_time):
        session = self.session
        population = Population(pop_name)
        population.creation_time = creation_time
        schema = session.query(Schema).\
            filter(Schema.name == schema_name).first()
        population.schema = schema
        session.add(population)
        session.commit()
    
    def get_population_names(self, schema_name):
        session = self.session
        schema = session.query(Schema).\
                    filter(Schema.name == schema_name).first()
        pop_names = []
        for population in schema.populations:
            pop_names.append(population.name)
        session.commit()
        return pop_names   

    def set_task(self, task_name, file_name, class_name, count=None):
        session = self.session
        population = self.get_pop_obj()
        task = Task(task_name, file_name, class_name, count)
        task.population = population
        session.add(task)            
        session.commit()
        
    def get_tasks(self, schema_name, pop_name):
        task_names = self.get_task_names(schema_name, pop_name)
        res = {}
        for task_name in task_names:
            task = self.get_task_obj(schema_name, pop_name, task_name)
            res[task_name] = task.get_dict()
        return res

    def get_task_filenames(self, schema_name, pop_name):
        session = self.session
        tasks = session.query(Task).join(Population).join(Schema).\
                            filter(Schema.name == schema_name).\
                            filter(Population.name == pop_name).all()
        task_filenames = []
        #print task        
        for task in tasks:
            task_filenames.append(task.file_name)
        session.commit()
        return task_filenames
            
    def get_task_names(self, schema_name, pop_name):
        session = self.session
        tasks = session.query(Task).\
                            join(Population).join(Schema).\
                            filter(Schema.name == schema_name).\
                            filter(Population.name == pop_name).all()
        task_names = []
        for task in tasks:
            task_names.append(task.name)
        session.commit()
        return task_names

def start_services():
    my_server = ThreadedServer(DBService, auto_register=True, 
                               protocol_config={
                                 "allow_pickle":True,
                                 "allow_custom_instantiation":True,
                                 "allow_all_attrs":True})
    my_server.start()

def initialize_databases():
    global engine, Session
    config_db = "sqlite:///%s/config.db" %constants.DATABASE_DIR
    engine = create_engine(config_db, echo=False, pool_size=50)
    Session = sessionmaker(engine)
    Base.metadata.drop_all(engine)
    Base.metadata.create_all(engine)

def main():
    initialize_databases()
    start_services()

if __name__ == "__main__":
    main()    
